{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "e29e2b7a",
      "metadata": {},
      "source": [
        "# Do you have a Gurobi WLS?\n",
        "This notebook can be ran using the limited licese but at a smaller level of detail. If you have a Gurobi WLS, you can go to a version of this notebook equipped for that license by [clicking here](https://colab.research.google.com/github/Gurobi/modeling-examples/blob/master/price_optimization/price_optimization_gurobiML_wls.ipynb).\n",
        "\n",
        "If you don't, no worries! You can still work through this version using a free limited Gurobi license."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "94a00ac5",
      "metadata": {
        "id": "94a00ac5"
      },
      "source": [
        "# Part Two: Avocado Pricing and Supply Using Mathematical Optimization \n",
        "\n",
        "This is a second part of an example on price optimization: [How Much Is Too Much? Avocado Pricing and Supply Using Mathematical Optimization](https://github.com/Gurobi/modeling-examples/tree/master/price_optimization)\n",
        "\n",
        "In the first part, an ordinary linear regression model (OLS) was used to establish the relationship between price and demand based on data from the.... Part Two replaces the OLS model with a trained `Scikit-learn` model and uses the [Gurobi Machine Learning](https://gurobi-machinelearning.readthedocs.io/en/stable/#) package to embed it in a Gurobi optimization model.\n",
        "\n",
        "In this example, we will also use `gurobipy-pandas`, which is another Gurobi open-source package and serves as a convenient (and optional) wrapper to connect pandas with gurobipy. \n",
        "\n",
        "If you are already familiar with the example from the other notebook, you can\n",
        "jump directly to [building the regression model](#Part-2:-Predict-the-Sales)\n",
        "and then to [formulating the optimization problem](#Part-3:-Optimize-for-Price-and-Supply-of-Avocados).\n",
        "\n",
        "**Goal**: Develop a data science and decision-making pipeline for pricing and distribution of avocados to maximize revenue.\n",
        "\n",
        "To accomplish this, the notebook will walk trough three stages:\n",
        "\n",
        "1. A quick review of the [Hass Avocado Board](https://hassavocadoboard.com/) (HAB) data\n",
        "2. Build a prediction model for avocado demand as a function of price, region, year and seasonality.\n",
        "3. Design an optimization model that sets the optimal price and supply quantity to maximize the net revenue while incorporating transportation and costs."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "716ed7e0",
      "metadata": {
        "id": "716ed7e0"
      },
      "source": [
        "## Load the Packages and Prepare the Dataset\n",
        "\n",
        "Just as in the first example, we use real HAB sales data. \n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b1ff1bfa",
      "metadata": {
        "id": "b1ff1bfa"
      },
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "import warnings\n",
        "import numpy as np"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "408e66ab",
      "metadata": {
        "id": "408e66ab"
      },
      "source": [
        "The dataset from HAB contains sales data for the years 2019-2022. This data is augmented by a previous download from HAB available on\n",
        "[Kaggle](https://www.kaggle.com/datasets/timmate/avocado-prices-2020) with sales for the years 2015-2018.\n",
        "\n",
        "This notebook will skip a lot of the preprocessing from the first version of this example."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "-bbhXnJqFlu2",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 424
        },
        "id": "-bbhXnJqFlu2",
        "outputId": "bffc7347-2634-4622-c807-ca31e1bad6ed"
      },
      "outputs": [
        {
          "data": {
            "text/html": [
              "\n",
              "  <div id=\"df-60c85821-5b56-4910-911b-97dae4efc455\">\n",
              "    <div class=\"colab-df-container\">\n",
              "      <div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>date</th>\n",
              "      <th>units_sold</th>\n",
              "      <th>price</th>\n",
              "      <th>region</th>\n",
              "      <th>year</th>\n",
              "      <th>month</th>\n",
              "      <th>peak</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>2015-01-04</td>\n",
              "      <td>3.382800</td>\n",
              "      <td>1.020000</td>\n",
              "      <td>Great_Lakes</td>\n",
              "      <td>2015</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>2015-01-04</td>\n",
              "      <td>2.578275</td>\n",
              "      <td>1.100000</td>\n",
              "      <td>Midsouth</td>\n",
              "      <td>2015</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>2015-01-04</td>\n",
              "      <td>5.794411</td>\n",
              "      <td>0.890000</td>\n",
              "      <td>West</td>\n",
              "      <td>2015</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>2015-01-04</td>\n",
              "      <td>3.204112</td>\n",
              "      <td>0.980000</td>\n",
              "      <td>Southeast</td>\n",
              "      <td>2015</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>2015-01-04</td>\n",
              "      <td>0.321824</td>\n",
              "      <td>1.050000</td>\n",
              "      <td>Northern_New_England</td>\n",
              "      <td>2015</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>...</th>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "      <td>...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3396</th>\n",
              "      <td>2022-05-15</td>\n",
              "      <td>0.445830</td>\n",
              "      <td>1.513707</td>\n",
              "      <td>Northern_New_England</td>\n",
              "      <td>2022</td>\n",
              "      <td>5</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3397</th>\n",
              "      <td>2022-05-15</td>\n",
              "      <td>4.150433</td>\n",
              "      <td>1.269883</td>\n",
              "      <td>SouthCentral</td>\n",
              "      <td>2022</td>\n",
              "      <td>5</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3398</th>\n",
              "      <td>2022-05-15</td>\n",
              "      <td>4.668815</td>\n",
              "      <td>1.644873</td>\n",
              "      <td>Northeast</td>\n",
              "      <td>2022</td>\n",
              "      <td>5</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3399</th>\n",
              "      <td>2022-05-15</td>\n",
              "      <td>32.745321</td>\n",
              "      <td>1.527357</td>\n",
              "      <td>Total_US</td>\n",
              "      <td>2022</td>\n",
              "      <td>5</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3401</th>\n",
              "      <td>2022-05-15</td>\n",
              "      <td>1.560202</td>\n",
              "      <td>1.541429</td>\n",
              "      <td>Plains</td>\n",
              "      <td>2022</td>\n",
              "      <td>5</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "<p>3402 rows × 7 columns</p>\n",
              "</div>\n",
              "      <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-60c85821-5b56-4910-911b-97dae4efc455')\"\n",
              "              title=\"Convert this dataframe to an interactive table.\"\n",
              "              style=\"display:none;\">\n",
              "        \n",
              "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "       width=\"24px\">\n",
              "    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
              "    <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
              "  </svg>\n",
              "      </button>\n",
              "      \n",
              "  <style>\n",
              "    .colab-df-container {\n",
              "      display:flex;\n",
              "      flex-wrap:wrap;\n",
              "      gap: 12px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert {\n",
              "      background-color: #E8F0FE;\n",
              "      border: none;\n",
              "      border-radius: 50%;\n",
              "      cursor: pointer;\n",
              "      display: none;\n",
              "      fill: #1967D2;\n",
              "      height: 32px;\n",
              "      padding: 0 0 0 0;\n",
              "      width: 32px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert:hover {\n",
              "      background-color: #E2EBFA;\n",
              "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "      fill: #174EA6;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert {\n",
              "      background-color: #3B4455;\n",
              "      fill: #D2E3FC;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert:hover {\n",
              "      background-color: #434B5C;\n",
              "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "      fill: #FFFFFF;\n",
              "    }\n",
              "  </style>\n",
              "\n",
              "      <script>\n",
              "        const buttonEl =\n",
              "          document.querySelector('#df-60c85821-5b56-4910-911b-97dae4efc455 button.colab-df-convert');\n",
              "        buttonEl.style.display =\n",
              "          google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "\n",
              "        async function convertToInteractive(key) {\n",
              "          const element = document.querySelector('#df-60c85821-5b56-4910-911b-97dae4efc455');\n",
              "          const dataTable =\n",
              "            await google.colab.kernel.invokeFunction('convertToInteractive',\n",
              "                                                     [key], {});\n",
              "          if (!dataTable) return;\n",
              "\n",
              "          const docLinkHtml = 'Like what you see? Visit the ' +\n",
              "            '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
              "            + ' to learn more about interactive tables.';\n",
              "          element.innerHTML = '';\n",
              "          dataTable['output_type'] = 'display_data';\n",
              "          await google.colab.output.renderOutput(dataTable, element);\n",
              "          const docLink = document.createElement('div');\n",
              "          docLink.innerHTML = docLinkHtml;\n",
              "          element.appendChild(docLink);\n",
              "        }\n",
              "      </script>\n",
              "    </div>\n",
              "  </div>\n",
              "  "
            ],
            "text/plain": [
              "           date  units_sold     price                region  year  month  peak\n",
              "0    2015-01-04    3.382800  1.020000           Great_Lakes  2015      1     0\n",
              "1    2015-01-04    2.578275  1.100000              Midsouth  2015      1     0\n",
              "2    2015-01-04    5.794411  0.890000                  West  2015      1     0\n",
              "3    2015-01-04    3.204112  0.980000             Southeast  2015      1     0\n",
              "4    2015-01-04    0.321824  1.050000  Northern_New_England  2015      1     0\n",
              "...         ...         ...       ...                   ...   ...    ...   ...\n",
              "3396 2022-05-15    0.445830  1.513707  Northern_New_England  2022      5     1\n",
              "3397 2022-05-15    4.150433  1.269883          SouthCentral  2022      5     1\n",
              "3398 2022-05-15    4.668815  1.644873             Northeast  2022      5     1\n",
              "3399 2022-05-15   32.745321  1.527357              Total_US  2022      5     1\n",
              "3401 2022-05-15    1.560202  1.541429                Plains  2022      5     1\n",
              "\n",
              "[3402 rows x 7 columns]"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "data_url = \"https://raw.githubusercontent.com/Gurobi/modeling-examples/master/price_optimization/\"\n",
        "avocado = pd.read_csv(data_url+\"HAB_data_2015to2022.csv\")\n",
        "avocado[\"date\"] = pd.to_datetime(avocado[\"date\"])\n",
        "avocado = avocado.sort_values(by=\"date\")\n",
        "avocado"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3a91caa2",
      "metadata": {
        "id": "3a91caa2"
      },
      "source": [
        "One of the regions in the above data frame is `Total_US`, so we can create a list of regions, excluding the total, which can be used to subset the data now. It'll be used later in the example as well."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "c2f6cc22",
      "metadata": {
        "id": "c2f6cc22"
      },
      "outputs": [],
      "source": [
        "regions = [\n",
        "    \"Great_Lakes\",\n",
        "    \"Midsouth\",\n",
        "    \"Northeast\",\n",
        "    \"Northern_New_England\",\n",
        "    \"SouthCentral\",\n",
        "    \"Southeast\",\n",
        "    \"West\",\n",
        "    \"Plains\"\n",
        "]\n",
        "df = avocado[avocado.region.isin(regions)]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5186fd3e",
      "metadata": {
        "id": "5186fd3e"
      },
      "source": [
        "## Predict the Sales\n",
        "\n",
        "In the first instance of this example, further analysis was done on the input data along with a few visualizations. Here, we will go directly to the predicive model training, starting with a random split of the dataset into $80\\%$ training and $20\\%$ testing data."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "866c540b",
      "metadata": {
        "id": "866c540b"
      },
      "outputs": [],
      "source": [
        "from sklearn.model_selection import train_test_split\n",
        "\n",
        "X = df[[\"region\", \"price\", \"year\", \"peak\"]]\n",
        "y = df[\"units_sold\"]\n",
        "# Split the data for training and testing\n",
        "X_train, X_test, y_train, y_test = train_test_split(\n",
        "    X, y, train_size=0.8, random_state=1\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "071154e3",
      "metadata": {
        "id": "071154e3"
      },
      "source": [
        "Note that the region is a categorical variable and we will transform that variable using Scikit Learn's `OneHotEncoder`. We also use a standard scaler for prices and year index, combining all of the ese with `Column Transformer` built using `make_column_transformer`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "71c7c565",
      "metadata": {
        "id": "71c7c565"
      },
      "outputs": [],
      "source": [
        "from sklearn.preprocessing import OneHotEncoder\n",
        "from sklearn.preprocessing import StandardScaler\n",
        "from sklearn.compose import make_column_transformer\n",
        "from sklearn.linear_model import LinearRegression\n",
        "from sklearn.pipeline import make_pipeline\n",
        "from sklearn.metrics import r2_score\n",
        "\n",
        "feat_transform = make_column_transformer(\n",
        "    (OneHotEncoder(drop=\"first\"), [\"region\"]),\n",
        "    (StandardScaler(), [\"price\", \"year\"]),\n",
        "    (\"passthrough\", [\"peak\"]),\n",
        "    verbose_feature_names_out=False,\n",
        "    remainder='drop'\n",
        ")"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "65b6bcaa",
      "metadata": {
        "id": "65b6bcaa"
      },
      "source": [
        "The regression model is a pipeline consisting of the `Column Transformer` and the type of model we want to use for the regression. For comparison, we'll stick with a linear regression."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "29AoNyWVdfH0",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "29AoNyWVdfH0",
        "outputId": "c602f200-f721-4d66-c70c-2670a223c9d8"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The R^2 value in the test set is 0.9083\n"
          ]
        }
      ],
      "source": [
        "reg = make_pipeline(feat_transform, LinearRegression())\n",
        "reg.fit(X_train, y_train)\n",
        "\n",
        "# Get R^2 from test data\n",
        "y_pred = reg.predict(X_test)\n",
        "print(f\"The R^2 value in the test set is {np.round(r2_score(y_test, y_pred),5)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3291d28d",
      "metadata": {
        "id": "3291d28d"
      },
      "source": [
        "We can observe a good $R^2$ value in the test set. We will now train the fit to the full dataset."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "1b956503",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "1b956503",
        "outputId": "93177781-34fd-4a72-d16a-9e074bf2e368"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The R^2 value in the full dataset is 0.90667\n"
          ]
        }
      ],
      "source": [
        "reg.fit(X, y)\n",
        "\n",
        "y_pred_full = reg.predict(X)\n",
        "print(f\"The R^2 value in the full dataset is {np.round(r2_score(y, y_pred_full),5)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "93868dba",
      "metadata": {
        "id": "93868dba"
      },
      "source": [
        "## Optimize for Price and Supply of Avocados\n",
        "\n",
        "Here is a quick review of notation for the formulation of the mathematical optimization model. The subscript $r$ will be used to denote each region.\n",
        "### Input parameters\n",
        "- $d(p,r)$: predicted demand in region $r$ when the avocado price is $p$\n",
        "- $B$: available avocados to be distributed across the regions\n",
        "- $c_{waste}$: cost ($\\$$) per wasted avocado\n",
        "- $c^r_{transport}$: cost ($\\$$) of transporting a avocado to region $r$\n",
        "- $a_{min},a_{max}$: minimum and maximum price ($\\$$) per avocado\n",
        "  $r$\n",
        "- $b^r_{min},b^r_{max}$: minimum and maximum number of avocados allocated to region $r$\n",
        "\n",
        "The following code sets values for these parameters. Feel free to adjust these to see how the aolution to the optimization model will change.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "1f293b80",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 300
        },
        "id": "1f293b80",
        "outputId": "5d74e7a3-f705-4bcf-aec5-b678b15f8656"
      },
      "outputs": [
        {
          "data": {
            "text/html": [
              "\n",
              "  <div id=\"df-c808bbd5-0ae0-4818-97e6-b4eb3c271999\">\n",
              "    <div class=\"colab-df-container\">\n",
              "      <div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>transport_cost</th>\n",
              "      <th>min_delivery</th>\n",
              "      <th>max_delivery</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>Great_Lakes</th>\n",
              "      <td>0.3</td>\n",
              "      <td>2.063574</td>\n",
              "      <td>7.094765</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Midsouth</th>\n",
              "      <td>0.1</td>\n",
              "      <td>1.845443</td>\n",
              "      <td>6.168572</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Northeast</th>\n",
              "      <td>0.4</td>\n",
              "      <td>2.364424</td>\n",
              "      <td>8.836406</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Northern_New_England</th>\n",
              "      <td>0.5</td>\n",
              "      <td>0.219690</td>\n",
              "      <td>0.917984</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>SouthCentral</th>\n",
              "      <td>0.3</td>\n",
              "      <td>3.687130</td>\n",
              "      <td>10.323175</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Southeast</th>\n",
              "      <td>0.2</td>\n",
              "      <td>2.197764</td>\n",
              "      <td>7.810475</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>West</th>\n",
              "      <td>0.2</td>\n",
              "      <td>3.260102</td>\n",
              "      <td>11.274749</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Plains</th>\n",
              "      <td>0.2</td>\n",
              "      <td>1.058938</td>\n",
              "      <td>3.575499</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>\n",
              "      <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-c808bbd5-0ae0-4818-97e6-b4eb3c271999')\"\n",
              "              title=\"Convert this dataframe to an interactive table.\"\n",
              "              style=\"display:none;\">\n",
              "        \n",
              "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "       width=\"24px\">\n",
              "    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
              "    <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
              "  </svg>\n",
              "      </button>\n",
              "      \n",
              "  <style>\n",
              "    .colab-df-container {\n",
              "      display:flex;\n",
              "      flex-wrap:wrap;\n",
              "      gap: 12px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert {\n",
              "      background-color: #E8F0FE;\n",
              "      border: none;\n",
              "      border-radius: 50%;\n",
              "      cursor: pointer;\n",
              "      display: none;\n",
              "      fill: #1967D2;\n",
              "      height: 32px;\n",
              "      padding: 0 0 0 0;\n",
              "      width: 32px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert:hover {\n",
              "      background-color: #E2EBFA;\n",
              "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "      fill: #174EA6;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert {\n",
              "      background-color: #3B4455;\n",
              "      fill: #D2E3FC;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert:hover {\n",
              "      background-color: #434B5C;\n",
              "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "      fill: #FFFFFF;\n",
              "    }\n",
              "  </style>\n",
              "\n",
              "      <script>\n",
              "        const buttonEl =\n",
              "          document.querySelector('#df-c808bbd5-0ae0-4818-97e6-b4eb3c271999 button.colab-df-convert');\n",
              "        buttonEl.style.display =\n",
              "          google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "\n",
              "        async function convertToInteractive(key) {\n",
              "          const element = document.querySelector('#df-c808bbd5-0ae0-4818-97e6-b4eb3c271999');\n",
              "          const dataTable =\n",
              "            await google.colab.kernel.invokeFunction('convertToInteractive',\n",
              "                                                     [key], {});\n",
              "          if (!dataTable) return;\n",
              "\n",
              "          const docLinkHtml = 'Like what you see? Visit the ' +\n",
              "            '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
              "            + ' to learn more about interactive tables.';\n",
              "          element.innerHTML = '';\n",
              "          dataTable['output_type'] = 'display_data';\n",
              "          await google.colab.output.renderOutput(dataTable, element);\n",
              "          const docLink = document.createElement('div');\n",
              "          docLink.innerHTML = docLinkHtml;\n",
              "          element.appendChild(docLink);\n",
              "        }\n",
              "      </script>\n",
              "    </div>\n",
              "  </div>\n",
              "  "
            ],
            "text/plain": [
              "                      transport_cost  min_delivery  max_delivery\n",
              "Great_Lakes                      0.3      2.063574      7.094765\n",
              "Midsouth                         0.1      1.845443      6.168572\n",
              "Northeast                        0.4      2.364424      8.836406\n",
              "Northern_New_England             0.5      0.219690      0.917984\n",
              "SouthCentral                     0.3      3.687130     10.323175\n",
              "Southeast                        0.2      2.197764      7.810475\n",
              "West                             0.2      3.260102     11.274749\n",
              "Plains                           0.2      1.058938      3.575499"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Sets and parameters\n",
        "B = 35  # total amount of avocado supply\n",
        "\n",
        "peak_or_not = 1  # 1 if it is the peak season; 0 if isn't\n",
        "year = 2022\n",
        "\n",
        "c_waste = 0.1  # the cost ($) of wasting an avocado\n",
        "\n",
        "# the cost of transporting an avocado\n",
        "c_transport = pd.Series(\n",
        "    {\n",
        "        \"Great_Lakes\": 0.3,\n",
        "        \"Midsouth\": 0.1,\n",
        "        \"Northeast\": 0.4,\n",
        "        \"Northern_New_England\": 0.5,\n",
        "        \"SouthCentral\": 0.3,\n",
        "        \"Southeast\": 0.2,\n",
        "        \"West\": 0.2,\n",
        "        \"Plains\": 0.2,\n",
        "    }, name='transport_cost'\n",
        ")\n",
        "c_transport = c_transport.loc[regions]\n",
        "\n",
        "a_min = 0  # minimum avocado price \n",
        "a_max = 3  # maximum avocado price\n",
        "\n",
        "# Get the lower and upper bounds from the dataset for the price and the number of products to be stocked\n",
        "data = pd.concat([c_transport,\n",
        "                  df.groupby(\"region\")[\"units_sold\"].min().rename('min_delivery'),\n",
        "                  df.groupby(\"region\")[\"units_sold\"].max().rename('max_delivery')], axis=1)\n",
        "\n",
        "data"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c6WbGctoAYUw",
      "metadata": {
        "id": "c6WbGctoAYUw"
      },
      "source": [
        "#### Install and import Gurobi packages"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "Z7eyw2klAAcG",
      "metadata": {
        "id": "Z7eyw2klAAcG"
      },
      "outputs": [],
      "source": [
        "%pip install gurobipy_pandas\n",
        "%pip install gurobi-machinelearning\n",
        "import gurobipy_pandas as gppd\n",
        "from gurobi_ml import add_predictor_constr"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5eaf9769",
      "metadata": {
        "id": "5eaf9769"
      },
      "source": [
        "### Create dataframe for the fixed features of the regression\n",
        "\n",
        "We now start creating the input of the regression in the optimization models with the features that are fixed.\n",
        "\n",
        "We use gurobipy-pandas that help to more easily create gurobipy models using pandas data."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ebb83dc2",
      "metadata": {
        "id": "ebb83dc2"
      },
      "source": [
        "First, create a dataframe with the features that are fixed in our optimization problem.\n",
        "It is indexed by the regions (we want to use one regression to predict demand for each region) and has the 3\n",
        "columns corresponding to the fixed features:\n",
        "\n",
        "* `year`\n",
        "* `peak` with the value of `peak_or_not`\n",
        "* `region` that repeat the names of the regions.\n",
        "\n",
        "Let's display the dataframe to make sure it is correct."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "e879833f",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 300
        },
        "id": "e879833f",
        "outputId": "88107112-9fa8-4d30-da95-e445328b3669"
      },
      "outputs": [
        {
          "data": {
            "text/html": [
              "\n",
              "  <div id=\"df-98fea3c5-9941-491b-a7c6-5361f1edd13a\">\n",
              "    <div class=\"colab-df-container\">\n",
              "      <div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>year</th>\n",
              "      <th>peak</th>\n",
              "      <th>region</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>Great_Lakes</th>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "      <td>Great_Lakes</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Midsouth</th>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "      <td>Midsouth</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Northeast</th>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "      <td>Northeast</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Northern_New_England</th>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "      <td>Northern_New_England</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>SouthCentral</th>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "      <td>SouthCentral</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Southeast</th>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "      <td>Southeast</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>West</th>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "      <td>West</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Plains</th>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "      <td>Plains</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>\n",
              "      <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-98fea3c5-9941-491b-a7c6-5361f1edd13a')\"\n",
              "              title=\"Convert this dataframe to an interactive table.\"\n",
              "              style=\"display:none;\">\n",
              "        \n",
              "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "       width=\"24px\">\n",
              "    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
              "    <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
              "  </svg>\n",
              "      </button>\n",
              "      \n",
              "  <style>\n",
              "    .colab-df-container {\n",
              "      display:flex;\n",
              "      flex-wrap:wrap;\n",
              "      gap: 12px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert {\n",
              "      background-color: #E8F0FE;\n",
              "      border: none;\n",
              "      border-radius: 50%;\n",
              "      cursor: pointer;\n",
              "      display: none;\n",
              "      fill: #1967D2;\n",
              "      height: 32px;\n",
              "      padding: 0 0 0 0;\n",
              "      width: 32px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert:hover {\n",
              "      background-color: #E2EBFA;\n",
              "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "      fill: #174EA6;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert {\n",
              "      background-color: #3B4455;\n",
              "      fill: #D2E3FC;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert:hover {\n",
              "      background-color: #434B5C;\n",
              "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "      fill: #FFFFFF;\n",
              "    }\n",
              "  </style>\n",
              "\n",
              "      <script>\n",
              "        const buttonEl =\n",
              "          document.querySelector('#df-98fea3c5-9941-491b-a7c6-5361f1edd13a button.colab-df-convert');\n",
              "        buttonEl.style.display =\n",
              "          google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "\n",
              "        async function convertToInteractive(key) {\n",
              "          const element = document.querySelector('#df-98fea3c5-9941-491b-a7c6-5361f1edd13a');\n",
              "          const dataTable =\n",
              "            await google.colab.kernel.invokeFunction('convertToInteractive',\n",
              "                                                     [key], {});\n",
              "          if (!dataTable) return;\n",
              "\n",
              "          const docLinkHtml = 'Like what you see? Visit the ' +\n",
              "            '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
              "            + ' to learn more about interactive tables.';\n",
              "          element.innerHTML = '';\n",
              "          dataTable['output_type'] = 'display_data';\n",
              "          await google.colab.output.renderOutput(dataTable, element);\n",
              "          const docLink = document.createElement('div');\n",
              "          docLink.innerHTML = docLinkHtml;\n",
              "          element.appendChild(docLink);\n",
              "        }\n",
              "      </script>\n",
              "    </div>\n",
              "  </div>\n",
              "  "
            ],
            "text/plain": [
              "                      year  peak                region\n",
              "Great_Lakes           2022     1           Great_Lakes\n",
              "Midsouth              2022     1              Midsouth\n",
              "Northeast             2022     1             Northeast\n",
              "Northern_New_England  2022     1  Northern_New_England\n",
              "SouthCentral          2022     1          SouthCentral\n",
              "Southeast             2022     1             Southeast\n",
              "West                  2022     1                  West\n",
              "Plains                2022     1                Plains"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "feats = pd.DataFrame(\n",
        "    data={\n",
        "        \"year\": year,\n",
        "        \"peak\": peak_or_not,\n",
        "        \"region\": regions,\n",
        "    },\n",
        "    index=regions\n",
        ")\n",
        "feats"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5103dcd2",
      "metadata": {
        "id": "5103dcd2"
      },
      "source": [
        "### Decision Variables\n",
        "\n",
        "Let us now define the decision variables. In our model, we want to store the\n",
        "price and number of avocados allocated to each region. We also want variables\n",
        "that track how many avocados are predicted to be sold and how many are predicted\n",
        "to be wasted. The following notation is used to model these decision variables.\n",
        "\n",
        "- $p$ the price of an avocado ($\\$$) in each region\n",
        "- $x$ the number of avocados supplied to each region\n",
        "- $s$ the predicted number of avocados sold in each region\n",
        "- $w$ the predicted number of avocados wasted in each region\n",
        "- $d$ the predicted demand in each region\n",
        "\n",
        "All those variables are created using gurobipy-pandas, with the function `gppd.add_vars` they are given the same index as the `data` dataframe."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "5da3e84b",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "5da3e84b",
        "outputId": "dd655d28-dfbe-49e5-fd3e-015a3674fee6"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Restricted license - for non-production use only - expires 2024-10-28\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "Great_Lakes                      <gurobi.Var price[Great_Lakes]>\n",
              "Midsouth                            <gurobi.Var price[Midsouth]>\n",
              "Northeast                          <gurobi.Var price[Northeast]>\n",
              "Northern_New_England    <gurobi.Var price[Northern_New_England]>\n",
              "SouthCentral                    <gurobi.Var price[SouthCentral]>\n",
              "Southeast                          <gurobi.Var price[Southeast]>\n",
              "West                                    <gurobi.Var price[West]>\n",
              "Plains                                <gurobi.Var price[Plains]>\n",
              "Name: price, dtype: object"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import gurobipy as gp\n",
        "\n",
        "m = gp.Model(\"Avocado_Price_Allocation\")\n",
        "\n",
        "p = gppd.add_vars(m, data, name=\"price\", lb=a_min, ub=a_max) # price of an avocado for each region\n",
        "x = gppd.add_vars(m, data, name=\"x\", lb='min_delivery', ub='max_delivery') # number of avocados supplied to each reagion\n",
        "s = gppd.add_vars(m, data, name=\"s\") # predicted amount of sales in each region for the given price\n",
        "w = gppd.add_vars(m, data, name=\"w\") # excess wasteage in each region\n",
        "d = gppd.add_vars(m, data, lb=-gp.GRB.INFINITY, name=\"demand\") # Add variables for the regression\n",
        "\n",
        "m.update()\n",
        "\n",
        "# Display one of the variables\n",
        "p"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a3bf602",
      "metadata": {
        "id": "5a3bf602"
      },
      "source": [
        "### Add the Supply Constraint\n",
        "\n",
        "We now introduce the constraints. The first constraint is to make sure that the\n",
        "total number of avocados supplied is equal to $B$, which can be mathematically\n",
        "expressed as follows.\n",
        "\n",
        "\\begin{align*} \\sum_{r} x_r &= B \\end{align*}\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "071c45f9",
      "metadata": {
        "id": "071c45f9"
      },
      "outputs": [],
      "source": [
        "m.addConstr(x.sum() == B)\n",
        "m.update()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "59009e4d",
      "metadata": {
        "id": "59009e4d"
      },
      "source": [
        "### Add Constraints That Define Sales Quantity\n",
        "\n",
        "As a quick reminder, the sales quantity is the minimum of the allocated quantity and the predicted demand, i.e., $s_r = \\min \\{x_r,d_r(p_r)\\}$ This relationship can be modeled by the following two constraints for each region $r$.\n",
        "\n",
        "\\begin{align*} s_r &\\leq x_r  \\\\\n",
        "s_r &\\leq d(p_r,r) \\end{align*}\n",
        "\n",
        "In this case, we use gurobipy-pandas `add_constrs` function, which is intuitive to use given the inequalities above."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "c5323898",
      "metadata": {
        "id": "c5323898"
      },
      "outputs": [],
      "source": [
        "gppd.add_constrs(m, s, gp.GRB.LESS_EQUAL, x)\n",
        "gppd.add_constrs(m, s, gp.GRB.LESS_EQUAL, d)\n",
        "m.update()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bf71c2f4",
      "metadata": {
        "id": "bf71c2f4"
      },
      "source": [
        "### Add the Wastage Constraints\n",
        "\n",
        "Finally, we should define the predicted wastage in each region, given by the\n",
        "supplied quantity that is not predicted to be sold. We can express this\n",
        "mathematically for each region $r$.\n",
        "\n",
        "\\begin{align*} w_r &= x_r - s_r \\end{align*}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "e3a28ede",
      "metadata": {
        "id": "e3a28ede"
      },
      "outputs": [],
      "source": [
        "gppd.add_constrs(m, w, gp.GRB.EQUAL, x - s)\n",
        "m.update()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0614c0c1",
      "metadata": {
        "id": "0614c0c1"
      },
      "source": [
        "### Add the constraints to predict demand\n",
        "First, we create our full input for the predictor constraint. We concatenate the `p` variables and the fixed features. Remember that the predicted price is a function of region, year, and peak/off-peak season."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "efaf9cea",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 300
        },
        "id": "efaf9cea",
        "outputId": "873bc699-affb-4c07-ba2f-3696288dff56"
      },
      "outputs": [
        {
          "data": {
            "text/html": [
              "\n",
              "  <div id=\"df-099a0792-c19b-4e29-82a7-c1dc42411207\">\n",
              "    <div class=\"colab-df-container\">\n",
              "      <div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>region</th>\n",
              "      <th>price</th>\n",
              "      <th>year</th>\n",
              "      <th>peak</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>Great_Lakes</th>\n",
              "      <td>Great_Lakes</td>\n",
              "      <td>&lt;gurobi.Var price[Great_Lakes]&gt;</td>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Midsouth</th>\n",
              "      <td>Midsouth</td>\n",
              "      <td>&lt;gurobi.Var price[Midsouth]&gt;</td>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Northeast</th>\n",
              "      <td>Northeast</td>\n",
              "      <td>&lt;gurobi.Var price[Northeast]&gt;</td>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Northern_New_England</th>\n",
              "      <td>Northern_New_England</td>\n",
              "      <td>&lt;gurobi.Var price[Northern_New_England]&gt;</td>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>SouthCentral</th>\n",
              "      <td>SouthCentral</td>\n",
              "      <td>&lt;gurobi.Var price[SouthCentral]&gt;</td>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Southeast</th>\n",
              "      <td>Southeast</td>\n",
              "      <td>&lt;gurobi.Var price[Southeast]&gt;</td>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>West</th>\n",
              "      <td>West</td>\n",
              "      <td>&lt;gurobi.Var price[West]&gt;</td>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Plains</th>\n",
              "      <td>Plains</td>\n",
              "      <td>&lt;gurobi.Var price[Plains]&gt;</td>\n",
              "      <td>2022</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>\n",
              "      <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-099a0792-c19b-4e29-82a7-c1dc42411207')\"\n",
              "              title=\"Convert this dataframe to an interactive table.\"\n",
              "              style=\"display:none;\">\n",
              "        \n",
              "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "       width=\"24px\">\n",
              "    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
              "    <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
              "  </svg>\n",
              "      </button>\n",
              "      \n",
              "  <style>\n",
              "    .colab-df-container {\n",
              "      display:flex;\n",
              "      flex-wrap:wrap;\n",
              "      gap: 12px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert {\n",
              "      background-color: #E8F0FE;\n",
              "      border: none;\n",
              "      border-radius: 50%;\n",
              "      cursor: pointer;\n",
              "      display: none;\n",
              "      fill: #1967D2;\n",
              "      height: 32px;\n",
              "      padding: 0 0 0 0;\n",
              "      width: 32px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert:hover {\n",
              "      background-color: #E2EBFA;\n",
              "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "      fill: #174EA6;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert {\n",
              "      background-color: #3B4455;\n",
              "      fill: #D2E3FC;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert:hover {\n",
              "      background-color: #434B5C;\n",
              "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "      fill: #FFFFFF;\n",
              "    }\n",
              "  </style>\n",
              "\n",
              "      <script>\n",
              "        const buttonEl =\n",
              "          document.querySelector('#df-099a0792-c19b-4e29-82a7-c1dc42411207 button.colab-df-convert');\n",
              "        buttonEl.style.display =\n",
              "          google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "\n",
              "        async function convertToInteractive(key) {\n",
              "          const element = document.querySelector('#df-099a0792-c19b-4e29-82a7-c1dc42411207');\n",
              "          const dataTable =\n",
              "            await google.colab.kernel.invokeFunction('convertToInteractive',\n",
              "                                                     [key], {});\n",
              "          if (!dataTable) return;\n",
              "\n",
              "          const docLinkHtml = 'Like what you see? Visit the ' +\n",
              "            '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
              "            + ' to learn more about interactive tables.';\n",
              "          element.innerHTML = '';\n",
              "          dataTable['output_type'] = 'display_data';\n",
              "          await google.colab.output.renderOutput(dataTable, element);\n",
              "          const docLink = document.createElement('div');\n",
              "          docLink.innerHTML = docLinkHtml;\n",
              "          element.appendChild(docLink);\n",
              "        }\n",
              "      </script>\n",
              "    </div>\n",
              "  </div>\n",
              "  "
            ],
            "text/plain": [
              "                                    region  \\\n",
              "Great_Lakes                    Great_Lakes   \n",
              "Midsouth                          Midsouth   \n",
              "Northeast                        Northeast   \n",
              "Northern_New_England  Northern_New_England   \n",
              "SouthCentral                  SouthCentral   \n",
              "Southeast                        Southeast   \n",
              "West                                  West   \n",
              "Plains                              Plains   \n",
              "\n",
              "                                                         price  year  peak  \n",
              "Great_Lakes                    <gurobi.Var price[Great_Lakes]>  2022     1  \n",
              "Midsouth                          <gurobi.Var price[Midsouth]>  2022     1  \n",
              "Northeast                        <gurobi.Var price[Northeast]>  2022     1  \n",
              "Northern_New_England  <gurobi.Var price[Northern_New_England]>  2022     1  \n",
              "SouthCentral                  <gurobi.Var price[SouthCentral]>  2022     1  \n",
              "Southeast                        <gurobi.Var price[Southeast]>  2022     1  \n",
              "West                                  <gurobi.Var price[West]>  2022     1  \n",
              "Plains                              <gurobi.Var price[Plains]>  2022     1  "
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "m_feats = pd.concat([feats, p], axis=1)[[\"region\", \"price\", \"year\", \"peak\"]]\n",
        "m_feats"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f2dc88f",
      "metadata": {
        "id": "8f2dc88f"
      },
      "source": [
        "Now, we just call\n",
        "[add_predictor_constr](https://gurobi-machinelearning.readthedocs.io/en/stable/api/AbstractPredictorConstr.html#gurobi_ml.add_predictor_constr)\n",
        "to insert the constraints linking the features and the demand into the model `m`.\n",
        "\n",
        "It is important that you keep the columns in the order above, otherwise you will see an error. The columns must be in the same order as the training data. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "200c4cb9",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "200c4cb9",
        "outputId": "7b6fc0b8-b103-4179-a35d-f3b10c34471e"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Model for pipe1:\n",
            "88 variables\n",
            "24 constraints\n",
            "Input has shape (8, 4)\n",
            "Output has shape (8, 1)\n",
            "\n",
            "Pipeline has 2 steps:\n",
            "\n",
            "--------------------------------------------------------------------------------\n",
            "Step            Output Shape    Variables              Constraints              \n",
            "                                                Linear    Quadratic      General\n",
            "================================================================================\n",
            "col_trans1           (8, 10)           24           16            0            0\n",
            "\n",
            "lin_reg1              (8, 1)           64            8            0            0\n",
            "\n",
            "--------------------------------------------------------------------------------\n"
          ]
        }
      ],
      "source": [
        "pred_constr = add_predictor_constr(m, reg, m_feats, d)\n",
        "pred_constr.print_stats()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "frjbAbjDNwr1",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "frjbAbjDNwr1",
        "outputId": "14918634-849b-4c09-dc6f-c8783c8c12cf"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<gurobi.Model Continuous instance Avocado_Price_Allocation: 49 constrs, 128 vars, No parameter changes>"
            ]
          },
          "execution_count": 19,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "m"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e15e4ae",
      "metadata": {
        "id": "5e15e4ae"
      },
      "source": [
        "### Set the Objective\n",
        "\n",
        "The goal is to maximize the **net revenue**, which is the product of price and quantity, minus costs over all regions. This model assumes the purchase costs are fixed (since the amount $B$ is fixed) and are therefore not incorporated.\n",
        "\n",
        "Using the defined decision variables, the objective can be written as follows.\n",
        "\n",
        "\\begin{align} \\textrm{maximize} &  \\sum_{r}  (p_r * s_r - c_{waste} * w_r -\n",
        "c^r_{transport} * x_r)& \\end{align}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "703ff1b2",
      "metadata": {
        "id": "703ff1b2"
      },
      "outputs": [],
      "source": [
        "m.setObjective((p * s).sum() - c_waste * w.sum() - (c_transport * x).sum(),\n",
        "               gp.GRB.MAXIMIZE)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cadd1721",
      "metadata": {
        "id": "cadd1721"
      },
      "source": [
        "### Fire Up the Solver\n",
        "\n",
        "In our model, the objective is **quadratic** since we take the product of price\n",
        "and the predicted sales, both of which are variables. Maximizing a quadratic\n",
        "term is said to be **non-convex**, and we specify this by setting the value of\n",
        "the [Gurobi NonConvex\n",
        "parameter](https://www.gurobi.com/documentation/10.0/refman/nonconvex.html) to be\n",
        "$2$."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "06a42223",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "06a42223",
        "outputId": "761a0098-43eb-41a3-c17b-a119474e3599"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Set parameter NonConvex to value 2\n",
            "Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\n",
            "\n",
            "CPU model: Intel(R) Xeon(R) CPU @ 2.20GHz, instruction set [SSE2|AVX|AVX2]\n",
            "Thread count: 1 physical cores, 2 logical processors, using up to 2 threads\n",
            "\n",
            "Optimize a model with 49 rows, 128 columns and 184 nonzeros\n",
            "Model fingerprint: 0x35ccdb1d\n",
            "Model has 8 quadratic objective terms\n",
            "Coefficient statistics:\n",
            "  Matrix range     [2e-01, 3e+00]\n",
            "  Objective range  [1e-01, 5e-01]\n",
            "  QObjective range [2e+00, 2e+00]\n",
            "  Bounds range     [2e-01, 2e+03]\n",
            "  RHS range        [1e+00, 2e+03]\n",
            "Presolve removed 32 rows and 104 columns\n",
            "\n",
            "Continuous model is non-convex -- solving as a MIP\n",
            "\n",
            "Presolve removed 32 rows and 104 columns\n",
            "Presolve time: 0.00s\n",
            "Presolved: 34 rows, 33 columns, 81 nonzeros\n",
            "Presolved model has 8 bilinear constraint(s)\n",
            "Variable types: 33 continuous, 0 integer (0 binary)\n",
            "\n",
            "Root relaxation: objective 7.843697e+01, 31 iterations, 0.00 seconds (0.00 work units)\n",
            "\n",
            "    Nodes    |    Current Node    |     Objective Bounds      |     Work\n",
            " Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time\n",
            "\n",
            "     0     0   78.43697    0    8          -   78.43697      -     -    0s\n",
            "H    0     0                      40.6484495   78.43697  93.0%     -    0s\n",
            "H    0     0                      41.8774607   78.43697  87.3%     -    0s\n",
            "     0     0   46.17264    0    8   41.87746   46.17264  10.3%     -    0s\n",
            "     0     0   43.52071    0    8   41.87746   43.52071  3.92%     -    0s\n",
            "     0     0   42.16768    0    8   41.87746   42.16768  0.69%     -    0s\n",
            "     0     0   41.98099    0    8   41.87746   41.98099  0.25%     -    0s\n",
            "     0     0   41.91760    0    8   41.87746   41.91760  0.10%     -    0s\n",
            "     0     0   41.89197    0    8   41.87746   41.89197  0.03%     -    0s\n",
            "     0     2   41.89197    0    8   41.87746   41.89197  0.03%     -    0s\n",
            "H   73    53                      41.8774607   41.89149  0.03%   1.5    0s\n",
            "H  152    90                      41.8774608   41.88408  0.02%   1.3    0s\n",
            "H  512   253                      41.8774608   41.88321  0.01%   0.9    0s\n",
            "\n",
            "Cutting planes:\n",
            "  RLT: 25\n",
            "\n",
            "Explored 515 nodes (657 simplex iterations) in 0.29 seconds (0.02 work units)\n",
            "Thread count was 2 (of 2 available processors)\n",
            "\n",
            "Solution count 2: 41.8775 40.6484 \n",
            "\n",
            "Optimal solution found (tolerance 1.00e-04)\n",
            "Best objective 4.187746077116e+01, best bound 4.187990090853e+01, gap 0.0058%\n"
          ]
        }
      ],
      "source": [
        "m.Params.NonConvex = 2\n",
        "m.optimize()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b3474030",
      "metadata": {
        "id": "b3474030"
      },
      "source": [
        "The solver solved the optimization problem in less than a second. Let us now\n",
        "analyze the optimal solution by storing it in a Pandas dataframe."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "ec0d106b",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 335
        },
        "id": "ec0d106b",
        "outputId": "8e11f057-dbc6-4e3e-d0d7-746a0872da55"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            " The optimal net revenue: $41.877461 million\n"
          ]
        },
        {
          "data": {
            "text/html": [
              "\n",
              "  <div id=\"df-7c0a7868-847b-40e5-bfca-1565c37977af\">\n",
              "    <div class=\"colab-df-container\">\n",
              "      <div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>Price</th>\n",
              "      <th>Allocated</th>\n",
              "      <th>Sold</th>\n",
              "      <th>Wasted</th>\n",
              "      <th>Pred_demand</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>Great_Lakes</th>\n",
              "      <td>1.6639</td>\n",
              "      <td>3.4464</td>\n",
              "      <td>3.4464</td>\n",
              "      <td>0.0000</td>\n",
              "      <td>3.4464</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Midsouth</th>\n",
              "      <td>1.5588</td>\n",
              "      <td>6.1686</td>\n",
              "      <td>3.4353</td>\n",
              "      <td>2.7333</td>\n",
              "      <td>3.4353</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Northeast</th>\n",
              "      <td>2.0390</td>\n",
              "      <td>4.0527</td>\n",
              "      <td>4.0527</td>\n",
              "      <td>0.0000</td>\n",
              "      <td>4.0527</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Northern_New_England</th>\n",
              "      <td>1.4412</td>\n",
              "      <td>0.9180</td>\n",
              "      <td>0.9180</td>\n",
              "      <td>0.0000</td>\n",
              "      <td>0.9180</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>SouthCentral</th>\n",
              "      <td>2.0527</td>\n",
              "      <td>4.3033</td>\n",
              "      <td>4.3033</td>\n",
              "      <td>0.0000</td>\n",
              "      <td>4.3033</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Southeast</th>\n",
              "      <td>1.7464</td>\n",
              "      <td>3.8486</td>\n",
              "      <td>3.8486</td>\n",
              "      <td>0.0000</td>\n",
              "      <td>3.8486</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>West</th>\n",
              "      <td>2.2042</td>\n",
              "      <td>9.6133</td>\n",
              "      <td>4.8575</td>\n",
              "      <td>4.7558</td>\n",
              "      <td>4.8575</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>Plains</th>\n",
              "      <td>1.2021</td>\n",
              "      <td>2.6491</td>\n",
              "      <td>2.6491</td>\n",
              "      <td>0.0000</td>\n",
              "      <td>2.6491</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>\n",
              "      <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-7c0a7868-847b-40e5-bfca-1565c37977af')\"\n",
              "              title=\"Convert this dataframe to an interactive table.\"\n",
              "              style=\"display:none;\">\n",
              "        \n",
              "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "       width=\"24px\">\n",
              "    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
              "    <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
              "  </svg>\n",
              "      </button>\n",
              "      \n",
              "  <style>\n",
              "    .colab-df-container {\n",
              "      display:flex;\n",
              "      flex-wrap:wrap;\n",
              "      gap: 12px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert {\n",
              "      background-color: #E8F0FE;\n",
              "      border: none;\n",
              "      border-radius: 50%;\n",
              "      cursor: pointer;\n",
              "      display: none;\n",
              "      fill: #1967D2;\n",
              "      height: 32px;\n",
              "      padding: 0 0 0 0;\n",
              "      width: 32px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert:hover {\n",
              "      background-color: #E2EBFA;\n",
              "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "      fill: #174EA6;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert {\n",
              "      background-color: #3B4455;\n",
              "      fill: #D2E3FC;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert:hover {\n",
              "      background-color: #434B5C;\n",
              "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "      fill: #FFFFFF;\n",
              "    }\n",
              "  </style>\n",
              "\n",
              "      <script>\n",
              "        const buttonEl =\n",
              "          document.querySelector('#df-7c0a7868-847b-40e5-bfca-1565c37977af button.colab-df-convert');\n",
              "        buttonEl.style.display =\n",
              "          google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "\n",
              "        async function convertToInteractive(key) {\n",
              "          const element = document.querySelector('#df-7c0a7868-847b-40e5-bfca-1565c37977af');\n",
              "          const dataTable =\n",
              "            await google.colab.kernel.invokeFunction('convertToInteractive',\n",
              "                                                     [key], {});\n",
              "          if (!dataTable) return;\n",
              "\n",
              "          const docLinkHtml = 'Like what you see? Visit the ' +\n",
              "            '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
              "            + ' to learn more about interactive tables.';\n",
              "          element.innerHTML = '';\n",
              "          dataTable['output_type'] = 'display_data';\n",
              "          await google.colab.output.renderOutput(dataTable, element);\n",
              "          const docLink = document.createElement('div');\n",
              "          docLink.innerHTML = docLinkHtml;\n",
              "          element.appendChild(docLink);\n",
              "        }\n",
              "      </script>\n",
              "    </div>\n",
              "  </div>\n",
              "  "
            ],
            "text/plain": [
              "                       Price  Allocated    Sold  Wasted  Pred_demand\n",
              "Great_Lakes           1.6639     3.4464  3.4464  0.0000       3.4464\n",
              "Midsouth              1.5588     6.1686  3.4353  2.7333       3.4353\n",
              "Northeast             2.0390     4.0527  4.0527  0.0000       4.0527\n",
              "Northern_New_England  1.4412     0.9180  0.9180  0.0000       0.9180\n",
              "SouthCentral          2.0527     4.3033  4.3033  0.0000       4.3033\n",
              "Southeast             1.7464     3.8486  3.8486  0.0000       3.8486\n",
              "West                  2.2042     9.6133  4.8575  4.7558       4.8575\n",
              "Plains                1.2021     2.6491  2.6491  0.0000       2.6491"
            ]
          },
          "execution_count": 22,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "solution = pd.DataFrame(index=regions)\n",
        "\n",
        "solution[\"Price\"] = p.gppd.X\n",
        "solution[\"Allocated\"] = x.gppd.X\n",
        "solution[\"Sold\"] = s.gppd.X\n",
        "solution[\"Wasted\"] = w.gppd.X\n",
        "solution[\"Pred_demand\"] = d.gppd.X\n",
        "\n",
        "opt_revenue = m.ObjVal\n",
        "print(\"\\n The optimal net revenue: $%f million\" % opt_revenue)\n",
        "solution.round(4)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "34279917",
      "metadata": {
        "id": "34279917"
      },
      "source": [
        "We can also check the error in the estimate of the Gurobi solution for the regression model."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "353149d2",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "353149d2",
        "outputId": "9e353c91-ccdd-4db1-da24-18814aad3f87"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Maximum error in approximating the regression 1.77636e-15\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    \"Maximum error in approximating the regression {:.6}\".format(\n",
        "        np.max(pred_constr.get_error())\n",
        "    )\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c3b4a665",
      "metadata": {
        "id": "c3b4a665"
      },
      "source": [
        "This was in introductory look at using the Gurobi Machine Learning package. For more on this example, see the [Price Optimization example of Github](https://github.com/Gurobi/modeling-examples/tree/master/price_optimization)\n",
        "as well as how to work with the model interactively."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bc9fbba6",
      "metadata": {
        "id": "bc9fbba6",
        "nbsphinx": "hidden"
      },
      "source": [
        "Copyright © 2023 Gurobi Optimization, LLC"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "jupytext": {
      "formats": "ipynb///ipynb,myst///md:myst"
    },
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.9.7"
    },
    "license": {
      "full_text": "# Copyright © 2022 Gurobi Optimization, LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n# =============================================================================="
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
